package postgres

import (
	"context"
	"fmt"
	"go.uber.org/zap"
	"gorm.io/gorm"
	logger "ota/collector/logger"
	"ota/config"
	"ota/errors"
	"ota/model"
	"ota/model/entity"
	"ota/utils"
	"strings"
)

var UserUpgrade = &userUpgrade{&execTx{}}

type userUpgrade struct {
	IExecTx
}

func init() {
	Register(UserUpgrade)
}

func (a *userUpgrade) Init() {
	if config.Conf.AutoMigrate {
		p := &entity.UserUpgrade{}
		if dbOta.Migrator().HasTable(p) {
			logger.Debug("table already exist: ", zap.String("table", p.TableName()))
			return
		}
		if err := dbOta.AutoMigrate(p); err != nil {
			logger.Error("filed to create table please check config or manually create", zap.String("table", p.TableName()), zap.String("err", err.Error()))
		} else {
			logger.Info("create table successfully", zap.String("table", p.TableName()))
		}
	}
}

// Create
func (a *userUpgrade) Create(ctx context.Context, m *entity.UserUpgrade) (int64, error) {
	err := GetDB(ctx).Create(m).Error
	return m.Id, err
}

// CreateInBatch
func (a *userUpgrade) CreateInBatch(ctx context.Context, m []*entity.UserUpgrade, size int) error {
	err := GetDB(ctx).CreateInBatches(m, size).Error
	return err
}

// Find detail
func (a *userUpgrade) Find(ctx context.Context, in *model.UserUpgradeInfoRequest) (*entity.UserUpgrade, error) {
	e := &entity.UserUpgrade{}

	q := GetDB(ctx).Model(&entity.UserUpgrade{})

	if in.Id == 0 {
		return e, errors.New("condition illegal")
	}
	err := q.First(&e, in.Id).Error
	return e, err
}

// Update
func (a *userUpgrade) Update(ctx context.Context, id int64, dict map[string]interface{}) error {
	return GetDB(ctx).Model(&entity.UserUpgrade{}).Where("id = ?", id).Updates(dict).Error
}

func (a *userUpgrade) UpdateByPlanId(ctx context.Context, id int64, dict map[string]interface{}) error {
	return GetDB(ctx).Model(&entity.UserUpgrade{}).Where("plan_id = ?", id).Updates(dict).Error
}

func (a *userUpgrade) Updates(ctx context.Context, id []int64, dict map[string]interface{}) error {
	return GetDB(ctx).Model(&entity.UserUpgrade{}).Where("id IN ?", id).Updates(dict).Error
}

// Delete
func (a *userUpgrade) Delete(ctx context.Context, id int64) error {
	return GetDB(ctx).Delete(&entity.UserUpgrade{}, id).Error
}

// Delete
func (a *userUpgrade) DeleteByPlanId(ctx context.Context, planId int64) error {
	return GetDB(ctx).Delete(&entity.UserUpgrade{}, "plan_id = ?", planId).Error
}

// Statistics
func (a *userUpgrade) Statistics(ctx context.Context, in *model.UserUpgradeStatisticsRequest) (*model.UserUpgradeStatisticsResponse, error) {

	var (
		q    = GetDB(ctx).Model(&entity.UserUpgrade{})
		resp = &model.UserUpgradeStatisticsResponse{}
	)

	q = q.Select(
		"plan_id",
		fmt.Sprintf("SUM(CASE WHEN status = %v THEN 1 ELSE 0 END) AS prepareTotal", int(model.UpgradeResultPending)),
		fmt.Sprintf("SUM(CASE WHEN status = %v THEN 1 ELSE 0 END) AS upgradingTotal", int(model.UpgradeResultUpgrading)),
		fmt.Sprintf("SUM(CASE WHEN status = %v THEN 1 ELSE 0 END) AS successTotal", int(model.UpgradeResultSuccess)),
		fmt.Sprintf("SUM(CASE WHEN status = %v THEN 1 ELSE 0 END) AS failTotal", int(model.UpgradeResultFailure)),
		fmt.Sprintf("SUM(CASE WHEN status = %v THEN 1 ELSE 0 END) AS cancelTotal", int(model.UpgradeResultAbolishment)),
	).
		Group("plan_id")

	if in.PlanId != 0 {
		q = q.Where("plan_id = ?", in.PlanId)
	}
	if len(in.PlanIds) != 0 {
		q = q.Where("plan_id IN (?)", in.PlanIds)
	}

	query := q.Find(&resp.List)

	if err := query.Error; err != nil {
		return nil, err
	}
	return resp, nil
}

// List query list
func (a *userUpgrade) List(ctx context.Context, in *model.UserUpgradeListRequest, opts ...model.QueryOptions) (int, []*entity.UserUpgrade, error) {

	var (
		opt          = model.GetQueryOption(opts...)
		q            = GetDB(ctx).Model(&entity.UserUpgrade{})
		err          error
		total        int64
		userUpgrades []*entity.UserUpgrade
	)

	if in.Id != nil {

		q = q.Where("id = ?", in.Id)

	}

	if in.Ids != nil {

		q = q.Where("id IN ?", in.Ids)

	}

	if in.UserId != nil {

		q = q.Where("user_id = ?", in.UserId)

	}

	if in.UserName != nil {

		q = q.Where("user_name like ?", utils.SqlLike(in.UserName))

	}

	if in.PlanId != nil {

		q = q.Where("plan_id = ?", in.PlanId)

	}

	if len(in.PlanIds) > 0 {

		q = q.Where("plan_id IN ?", in.PlanIds)

	}

	if in.PlanName != nil {

		q = q.Where("plan_name like ?", utils.SqlLike(in.PlanName))

	}

	if in.EbikeId != nil {

		q = q.Where("ebike_id = ?", in.EbikeId)

	}

	if len(in.EbikeIds) > 0 {

		q = q.Where("ebike_id IN ?", in.EbikeIds)

	}

	if len(in.EbikeTypeIds) > 0 {

		q = q.Where("ebike_type_id IN ?", in.EbikeTypeIds)

	}

	if in.EbikeName != nil {

		q = q.Where("ebike_name like ?", utils.SqlLike(in.EbikeName))

	}

	if in.EbikeSnCode != nil {

		q = q.Where("ebike_sn_code like ?", utils.SqlLike(in.EbikeSnCode))

	}

	if len(in.UpgradeTypes) > 0 {
		q = q.Where("upgrade_type IN ?", in.UpgradeTypes)
	}

	if len(in.EbikeSnCodes) > 0 {

		var conds []string
		for _, v := range in.EbikeSnCodes {
			conds = append(conds, fmt.Sprintf("(ebike_sn_code like %v)", utils.SqlLike(&v)))
		}
		q = q.Where(strings.Join(conds, " OR "))

	}

	if in.EbikeTypeId != nil {

		q = q.Where("ebike_type_id = ?", in.EbikeTypeId)

	}

	if in.EbikeTypeName != nil {

		q = q.Where("ebike_type_name like ?", utils.SqlLike(in.EbikeTypeName))

	}

	if in.Detail != nil {

		q = q.Where("detail = ?", in.Detail)

	}

	if in.Progress != nil {

		q = q.Where("progress = ?", in.Progress)

	}

	if in.Status != nil {

		q = q.Where("status = ?", in.Status)

	}

	if in.UpgradingStatus != nil {

		q = q.Where("upgrading_status = ?", in.UpgradingStatus)

	}

	if len(in.Statuses) > 0 {

		q = q.Where("status IN ?", in.Statuses)

	}

	if in.FailureReason != nil {

		q = q.Where("failure_reason like ?", in.FailureReason)

	}

	if in.RetryCount != nil {

		q = q.Where("retry_count = ?", in.RetryCount)

	}

	if in.CreatedAt != nil {

		q = q.Where("created_at = ?", in.CreatedAt)

	}

	if in.UpdatedAt != nil {

		q = q.Where("updated_at = ?", in.UpdatedAt)

	}

	if len(opt.Conditions) > 0 {
		q = q.Where(strings.Join(opt.Conditions, " AND "))
	}

	if len(opt.SelectFields) > 0 {
		q = q.Select(opt.SelectFields)
	}

	if len(opt.OrderFields) > 0 {
		q = q.Order(fmt.Sprintf("%v", model.ParseOrder(opt.OrderFields)))
	}

	if len(opt.GroupBy) > 0 {
		q = q.Group(fmt.Sprintf("%v", strings.Join(opt.GroupBy, " ")))
	}

	b := !opt.OnlyCount && !opt.OnlyList
	if opt.OnlyCount || b {
		if err = q.Count(&total).Error; err != nil {
			return 0, nil, err
		}
	}

	if opt.OnlyList || b {
		if err = q.Limit(in.Size).Offset((in.Index - 1) * in.Size).Find(&userUpgrades).Error; err != nil {
			return 0, nil, err
		}
	}
	return int(total), userUpgrades, nil
}

func (a *userUpgrade) GetUpgrade(ctx context.Context, callback func(ctx context.Context) error) error {

	return nil
}

// ExecTransaction execute database transaction
func (a *userUpgrade) ExecTransaction(ctx context.Context, callback func(ctx context.Context) error) error {
	return GetDB(ctx).Transaction(func(tx *gorm.DB) error {
		ctx = context.WithValue(ctx, ContextTxKey, tx)
		return callback(ctx)
	})
}
